﻿#include "CentralCache.h"
#include "PageCache.h"
CentralCache CentralCache::_Instance;
//从central cache或 pagecache获取⼀个非空的span
Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
	//查看是否有未分配对象的span
	Span* it = list.Begin();
	while (it != list.End())
	{
		if (it->_freeList != nullptr)
		{
			return it;
		}
		else
		{
			it = it->_next;
		}
	}
	//先把central cache的桶锁解掉，这样不影响其他线程释放内存回来，避免被阻塞

	list._mutex.unlock();

	//走到这里说明没有空闲的span，只能找page cache要
	PageCache::GetInstance()->_mutex.lock();
	Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
	span->_isUse = true;
	span->_objSize = size;
	PageCache::GetInstance()->_mutex.unlock();

	//对获取的span进行切分，不需要加锁，因为其他线程访问不到这个Span
	
	//计算span大块内存的起始地址和大块内存的字节数
	char* start = (char*)(span->_pageId << PAGE_SHIFT);
	size_t bytes = span->_n << PAGE_SHIFT;
	char* end = start + bytes;
	//把大块内存切成自由链表链接起来
	span->_freeList = start;
	void* tail = span->_freeList;
	start += size;
	while (start < end)
	{
		NextObj(tail) = start;
		tail = start;
		start += size;
	}
	NextObj(tail) = nullptr;
	//切好Span以后，需要把Span挂到桶里面需要加锁
	list._mutex.lock();
	list.PushFront(span);
	return span;
}


// 从中⼼缓存获取⼀定数量的对象给thread cache
size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)
{
	size_t index = SizeClass::Index(size);
	_spanLists[index]._mutex.lock();

	Span* span = GetOneSpan(_spanLists[index],size);
	assert(span);
	assert(span->_freelist);
	//从Span中获取batchNum个小块内存对象
	//如果不够batchNum 有几个拿几个
	start = span->_freeList;
	end = start;
	size_t i = 0;
	size_t actualNum = 1;
	while(i < batchNum - 1 && NextObj(end) )
	{
		end = NextObj(end);
		i++;
		actualNum++;
	}
	span->_freeList = NextObj(end);
	NextObj(end) = nullptr;
	span->_useCount += actualNum;

	_spanLists[index]._mutex.unlock();
	return actualNum;
}
// 将一定数量的对象释放到span跨度
void CentralCache::ReleaseListToSpans(void* start, size_t size)
{
	size_t index = SizeClass::Index(size);
	_spanLists[index]._mutex.lock();
	while (start)
	{
		void* next = NextObj(start);
		Span* span = PageCache::GetInstance()->MapObjectToSpan(start);
		NextObj(start) = span->_freeList;
		span->_freeList = start;
		span->_useCount--;
		//说明这个Span切分出去的小块内存全都还回来了
		//这个Span就可以再回收给pagecache，pagecache 就可以尝试合并前后页
		if (span->_useCount == 0)
		{
			_spanLists[index].Erase(span);
			span->_freeList = nullptr;
			span->_prev = nullptr;
			span->_next = nullptr;
			_spanLists[index]._mutex.unlock();

			//释放Span给pagecache需要加锁，使用pagecache的锁
			//这是需要释放该Span所在centralcache的桶锁，避免其他线程申请释放内存被阻塞
			PageCache::GetInstance()->_mutex.lock();
			PageCache::GetInstance()->ReleaseSpanToPageCache(span);
			PageCache::GetInstance()->_mutex.unlock();

			_spanLists[index]._mutex.lock();

		}
		start = next;
	}
	_spanLists[index]._mutex.unlock();
}
